安卓语言切换流程
文章目录
Settings
在settings应用中,通过LocaleDragAndDropAdapter来实现语言列表拖拽切换语言的,在拖拽结束后会调用updateLocalesWhenAnimationStops表示当动画结束后,进行locale的更新操作
1 | public void updateLocalesWhenAnimationStops(final LocaleList localeList) { |
- LocalePicker.updateLocales(mLocalesToSetNext);当动画结束时,调用系统方法,更新locale,LocalePicker的位置:
android\frameworks\base\core\java\com\android\internal\app\LocalePicker.java
请求系统来更新系统的语言设置
1 | /** |
- 通过
am.updatePersistentConfiguration(config);
来进行配置更新和持久化,am的真正实现为 ActivityManagerService.java,先确认权限,
1 | @Override |
- 详细分析updateConfigurationLocked:
1 | /** |
- updateGlobalConfiguration:更新默认的global配置,并通知各个监听器,比较长的代码,注释解读:该方法主要做了以下事情
1 |
|
以上方法主要做了以下几件事情:
设置persist.sys.locale,持久化保存
发送SEND_LOCALE_TO_MOUNT_DAEMON_MSG消息通知,storageManager
调用mStackSupervisor的回调mStackSupervisor.onConfigurationChanged(mTempConfig);
1
2
3
4
5
6
7
8
9
10
11
12/**
* Notify that parent config changed and we need to update full configuration.
* @see #mFullConfiguration
*/
void onConfigurationChanged(Configuration newParentConfig) {
mFullConfiguration.setTo(newParentConfig);
mFullConfiguration.updateFrom(mOverrideConfiguration);
for (int i = getChildCount() - 1; i >= 0; --i) {
final ConfigurationContainer child = getChildAt(i);
child.onConfigurationChanged(mFullConfiguration);
}
}mUsageStatsService.reportConfigurationChange(mTempConfig,mUserController.getCurrentUserIdLocked()); 报告UsageEvents
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17@Override
public void reportConfigurationChange(Configuration config, int userId) {
if (config == null) {
Slog.w(TAG, "Configuration event reported with a null config");
return;
}
UsageEvents.Event event = new UsageEvents.Event();
event.mPackage = "android";
// This will later be converted to system time.
event.mTimeStamp = SystemClock.elapsedRealtime();
event.mEventType = UsageEvents.Event.CONFIGURATION_CHANGE;
event.mConfiguration = new Configuration(config);
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}mSystemThread.applyConfigurationToResources(mTempConfig);
ActivityThread.Java
1
2
3
4
5public final void applyConfigurationToResources(Configuration config) {
synchronized (mResourcesManager) {
mResourcesManager.applyConfigurationToResourcesLocked(config, null);
}
}ResourcesManager.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
@Nullable CompatibilityInfo compat) {
try {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#applyConfigurationToResourcesLocked");
if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
+ mResConfiguration.seq + ", newSeq=" + config.seq);
return false;
}
int changes = mResConfiguration.updateFrom(config);
// Things might have changed in display manager, so clear the cached displays.
mAdjustedDisplays.clear();
DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
if (compat != null && (mResCompatibilityInfo == null ||
!mResCompatibilityInfo.equals(compat))) {
mResCompatibilityInfo = compat;
changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
| ActivityInfo.CONFIG_SCREEN_SIZE
| ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
}
Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
ApplicationPackageManager.configurationChanged();
//Slog.i(TAG, "Configuration changed in " + currentPackageName());
Configuration tmpConfig = null;
for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
ResourcesKey key = mResourceImpls.keyAt(i);
WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
if (r != null) {
if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+ r + " config to: " + config);
int displayId = key.mDisplayId;
boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
DisplayMetrics dm = defaultDisplayMetrics;
final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
if (!isDefaultDisplay || hasOverrideConfiguration) {
if (tmpConfig == null) {
tmpConfig = new Configuration();
}
tmpConfig.setTo(config);
// Get new DisplayMetrics based on the DisplayAdjustments given
// to the ResourcesImpl. Update a copy if the CompatibilityInfo
// changed, because the ResourcesImpl object will handle the
// update internally.
DisplayAdjustments daj = r.getDisplayAdjustments();
if (compat != null) {
daj = new DisplayAdjustments(daj);
daj.setCompatibilityInfo(compat);
}
dm = getDisplayMetrics(displayId, daj);
if (!isDefaultDisplay) {
applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
}
if (hasOverrideConfiguration) {
tmpConfig.updateFrom(key.mOverrideConfiguration);
}
r.updateConfiguration(tmpConfig, dm, compat);
} else {
r.updateConfiguration(config, dm, compat);
}
//Slog.i(TAG, "Updated app resources " + v.getKey()
// + " " + r + ": " + r.getConfiguration());
} else {
//Slog.i(TAG, "Removing old resources " + v.getKey());
mResourceImpls.removeAt(i);
}
}
return changes != 0;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat); 更新资源配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22if ((configChanges & ActivityInfo.CONFIG_LOCALE) != 0) {
if (locales.size() > 1) {
// The LocaleList has changed. We must query the AssetManager's available
// Locales and figure out the best matching Locale in the new LocaleList.
String[] availableLocales = mAssets.getNonSystemLocales();
if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
// No app defined locales, so grab the system locales.
availableLocales = mAssets.getLocales();
if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
availableLocales = null;
}
}
if (availableLocales != null) {
final Locale bestLocale = locales.getFirstMatchWithEnglishSupported(
availableLocales);
if (bestLocale != null && bestLocale != locales.get(0)) {
mConfiguration.setLocales(new LocaleList(bestLocale, locales));
}
}
}
}ApplicationPackageManager.configurationChanged();清空缓存
1
2
3
4
5
6static void configurationChanged() {
synchronized (sSync) {
sIconCache.clear();
sStringCache.clear();
}
}app.thread.scheduleConfigurationChanged(configCopy);遍历每个activityThread,应用新的配置
ActivityThread.java
1
2
3
4public void scheduleConfigurationChanged(Configuration config) {
updatePendingConfiguration(config);
sendMessage(H.CONFIGURATION_CHANGED, config);
}CONFIGURATION_CHANGED:
1
2
3
4
5
6
7
8
9
10
11case CONFIGURATION_CHANGED:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi;
mUpdatingSystemConfig = true;
try {
handleConfigurationChanged((Configuration) msg.obj, null);
} finally {
mUpdatingSystemConfig = false;
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;ActivityThread.handleConfigurationChanged
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
int configDiff = 0;
// This flag tracks whether the new configuration is fundamentally equivalent to the
// existing configuration. This is necessary to determine whether non-activity
// callbacks should receive notice when the only changes are related to non-public fields.
// We do not gate calling {@link #performActivityConfigurationChanged} based on this flag
// as that method uses the same check on the activity config override as well.
final boolean equivalent = config != null && mConfiguration != null
&& (0 == mConfiguration.diffPublicOnly(config));
synchronized (mResourcesManager) {
if (mPendingConfiguration != null) {
if (!mPendingConfiguration.isOtherSeqNewer(config)) {
config = mPendingConfiguration;
mCurDefaultDisplayDpi = config.densityDpi;
updateDefaultDensity();
}
mPendingConfiguration = null;
}
if (config == null) {
return;
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: "
+ config);
mResourcesManager.applyConfigurationToResourcesLocked(config, compat);
updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
mResourcesManager.getConfiguration().getLocales());
if (mConfiguration == null) {
mConfiguration = new Configuration();
}
if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
return;
}
configDiff = mConfiguration.updateFrom(config);
config = applyCompatConfiguration(mCurDefaultDisplayDpi);
final Theme systemTheme = getSystemContext().getTheme();
if ((systemTheme.getChangingConfigurations() & configDiff) != 0) {
systemTheme.rebase();
}
final Theme systemUiTheme = getSystemUiContext().getTheme();
if ((systemUiTheme.getChangingConfigurations() & configDiff) != 0) {
systemUiTheme.rebase();
}
}
ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);
freeTextLayoutCachesIfNeeded(configDiff);
if (callbacks != null) {
final int N = callbacks.size();
for (int i=0; i<N; i++) {
ComponentCallbacks2 cb = callbacks.get(i);
if (cb instanceof Activity) {
// If callback is an Activity - call corresponding method to consider override
// config and avoid onConfigurationChanged if it hasn't changed.
Activity a = (Activity) cb;
performConfigurationChangedForActivity(mActivities.get(a.getActivityToken()),
config);
} else if (!equivalent) {
performConfigurationChanged(cb, config);
}
}
}
}
1 |
app.thread.scheduleConfigurationChanged(configCopy);
回到
ActivityThread.java
1 | public void scheduleConfigurationChanged(Configuration config) { |
CONFIGURATION_CHANGED:
1 |
|
ActivityThread.handleConfigurationChanged
1 | final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) { |
其中调用log打印
1 | 01-09 03:12:27.360: I/linlian(5221): LocaleDragAndDropAdapter.updateLocalesWhenAnimationStops() |